package com.zip;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.lang3.StringUtils;

public final class FileToZip {

	public static String suffixName = ".zip";


	/** 缓冲器大小 */
	private static final int BUFFER = 1024;

	/**
	 * 取相对路径
	 * 依据文件名和压缩源路径得到文件在压缩源路径下的相对路径
	 *
	 * @param dirPath 压缩源路径
	 * @param file
	 *
	 * @return 相对路径
	 */
	private static String getRelativePath(String dirPath, File file) {
		File   dir          = new File(dirPath);
		String relativePath = file.getName();

		while (true) {
			file = file.getParentFile();

			if (file == null) {
				break;
			}

			if (file.equals(dir)) {
				break;
			} else {
				relativePath = file.getName() + "/" + relativePath;
			}
		}    // end while

		return relativePath;
	}

	/**
	 * 取的给定源目录下的所有文件及空的子目录
	 * 递归实现
	 *
	 * @param srcFile
	 *
	 * @return
	 */
	private static List<File> getAllFiles(File srcFile) {
		List<File> fileList = new ArrayList<File>();
		File[]     tmp      = srcFile.listFiles();

		for (int i = 0; i < tmp.length; i++) {
			if (tmp[i].isFile()) {
				fileList.add(tmp[i]);
				System.out.println("add file: "+tmp[i].getName());
			}

			if (tmp[i].isDirectory()) {
				if (tmp[i].listFiles().length!=0){//若不是空目录，则递归添加其下的目录和文件
					fileList.addAll(getAllFiles(tmp[i]));
				}
				else{//若是空目录，则添加这个目录到fileList
					fileList.add(tmp[i]);
					System.out.println("add empty dir: "+tmp[i].getName());
				}
			}
		}    // end for


		return fileList;
	}



	/**
	 * 压缩方法
	 * （可以压缩空的子目录）
	 * @param srcPath 压缩源路径
	 * @param zipFileName   绝对路劲+文件名
	 *
	 * @return
	 */
	public static boolean fileToZip(String srcPath, String zipFileName) {
		File       srcFile    = new File(srcPath);
		List<File> fileList   = getAllFiles(srcFile);//所有要压缩的文件
		byte[]     buffer     = new byte[BUFFER];//缓冲器
		int        readLength = 0;//每次读出来的长度

		try(ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName))) {

			for (File file : fileList) {
				if (file.isFile()){//若是文件，则压缩这个文件
					ZipEntry zipEntry = new ZipEntry(getRelativePath(srcPath, file));
					zipEntry.setSize(file.length());
					zipEntry.setTime(file.lastModified());
					zipOutputStream.putNextEntry(zipEntry);
					try(InputStream inputStream = new BufferedInputStream(new FileInputStream(file));) {

						while ((readLength = inputStream.read(buffer, 0, BUFFER)) != -1) {
							zipOutputStream.write(buffer, 0, readLength);
						}
					}

				}

			}    // end for

		} catch (FileNotFoundException e) {
			System.out.println("zip fail!" + e.getMessage());
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			System.out.println("zip fail!" +e.getMessage());
			e.printStackTrace();
			return false;
		}
		System.out.println("zip success!");
		System.out.println(buffer.length);
		return true;
	}

	/**
	 * 压缩方法
	 * （可以压缩空的子目录）
	 * @param srcPath 压缩源路径
	 * @param zipFilePath 目标压缩文件
	 * @param fileName    文件名
	 *
	 * @return
	 */
	public static boolean fileToZip(String srcPath, String zipFilePath, String fileName) {
		if(StringUtils.isNotBlank(zipFilePath)){
			char lastChar = zipFilePath.charAt(zipFilePath.length()-1);
			if(lastChar != '/'){
				zipFilePath = zipFilePath+'/';
			}
		}
		String zipFileName = zipFilePath+fileName;
		return fileToZip(srcPath,zipFileName);
	}



	public static void deleteAllFilesOfDir(File path) {
		if (!path.exists())
			return;
		if (path.isFile()) {
			path.delete();
			return;
		}
		File[] files = path.listFiles();
		for (int i = 0; i < files.length; i++) {
			deleteAllFilesOfDir(files[i]);
		}
		path.delete();
	}

	/**
	 * 压缩文件目录
	 *
	 * @param source 源文件目录（单个文件和多层目录）
	 * @param destit 目标目录
	 */
	public static void zipFiles(String source, String destit) {
		File file = new File(source);
		try (FileOutputStream fileOutputStream = new FileOutputStream(destit);
				ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);) {
			if (file.isDirectory()) {
				directory(zipOutputStream, file, "");
			} else {
				zipFile(zipOutputStream, file, "");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 递归压缩目录结构
	 *
	 * @param zipOutputStream
	 * @param file
	 * @param parentFileName
	 */
	private static void directory(ZipOutputStream zipOutputStream, File file, String parentFileName) {
		File[] files = file.listFiles();
		String parentFileNameTemp = null;
		for (File fileTemp : files) {
			if (fileTemp.getName().indexOf(FileToZip.suffixName) != -1) {
				continue;
			}
			parentFileNameTemp = StringUtils.isEmpty(parentFileName) ? fileTemp.getName()
					: parentFileName + "/" + fileTemp.getName();
			if (fileTemp.isDirectory()) {
				directory(zipOutputStream, fileTemp, parentFileNameTemp);
			} else {
				zipFile(zipOutputStream, fileTemp, parentFileNameTemp);
			}
		}
	}

	private static void zipFile(ZipOutputStream zipOutputStream, File file, String parentFileName) {
		try (FileInputStream in = new FileInputStream(file);) {
			ZipEntry zipEntry = new ZipEntry(parentFileName);
			zipOutputStream.putNextEntry(zipEntry);
			int len;
			byte[] buf = new byte[8 * 1024];
			while ((len = in.read(buf)) != -1) {
				zipOutputStream.write(buf, 0, len);
			}
			zipOutputStream.closeEntry();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 解压zip压缩文件到指定目录
	 *
	 * @param zipPath zip压缩文件绝对路径
	 * @param descDir 指定的解压目录
	 */
	public static void unzipFile(String zipPath, String descDir) throws IOException {
		File zipFile = new File(zipPath);
		File pathFile = new File(descDir);
		unzipFile(zipFile, pathFile);
	}

	/**
	 * 解压zip压缩文件到指定目录
	 *
	 * @param zipPath zip压缩文件绝对路径
	 * @param descDir 指定的解压目录
	 */
	public static void unzipFile(File zipfile, File pathFile) throws IOException {
		try {
			if (!zipfile.exists()) {
				throw new IOException("要解压的压缩文件不存在");
			}

			if (!pathFile.exists()) {
				pathFile.mkdirs();
			}
			InputStream input = new FileInputStream(zipfile);
			unzipWithStream(input, pathFile.getPath());
		} catch (Exception e) {
			throw new IOException(e);
		}
	}

	/**
	 * 解压
	 *
	 * @param inputStream
	 * @param descDir
	 */
	public static void unzipWithStream(InputStream inputStream, String descDir) {
		if (!descDir.endsWith(File.separator)) {
			descDir = descDir + File.separator;
		}
		try (ZipInputStream zipInputStream = new ZipInputStream(inputStream, Charset.forName("GBK"))) {
            ZipEntry zipEntry;
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                String zipEntryNameStr = zipEntry.getName();
                
                String zipEntryName = zipEntryNameStr;
                if (zipEntryNameStr.contains("/")) {
                	System.out.println(zipEntryNameStr);
                    String str1 = zipEntryNameStr.substring(0, zipEntryNameStr.indexOf("/"));
                    zipEntryName = zipEntryNameStr.substring(str1.length() + 1);
                    System.err.println(zipEntryName);
                }
                String outPath = (descDir + zipEntryName).replace("\\\\", "/");
                //System.err.println(outPath);
                if(outPath.contains("/")) {
                	outPath = outPath.substring(0, outPath.lastIndexOf('/'));
                }
                File outFile = new File(outPath);
                if (!outFile.exists()) {
                    outFile.mkdirs();
                }
                if (new File(outPath).isDirectory()) {
                    continue;
                }
                writeFile(outPath, zipInputStream);
                zipInputStream.closeEntry();
            }
            System.out.println("======解压成功=======");
		} catch (IOException e) {
			System.out.println("压缩包处理异常，异常信息{}" + e);
		}
	}

	// 将流写到文件中
	public static void writeFile(String filePath, ZipInputStream zipInputStream) {
		try (OutputStream outputStream = new FileOutputStream(filePath)) {
			byte[] bytes = new byte[4096];
			int len;
			while ((len = zipInputStream.read(bytes)) != -1) {
				outputStream.write(bytes, 0, len);
			}
		} catch (IOException ex) {
			System.out.println("解压文件时，写出到文件出错");
		}
	}

}